/** 
 * @file   ClustalWSample.cpp
 * @author Scanagatta Mauro - Alberto Franco
 * 
 * Contains multiple alignment algorithm class implementation.
 */
#include <set>
#include <vector>
#include <sstream>
#include <iostream>

#include "ClustalWSample.h"
#include "NewickTree.h"
#include "NewickTreeFactory.h"

namespace Biopool
{

ClustalWSample::ClustalWSample(std::string newickEvolTree, 
                               std::vector<std::string> inSequences) : 
       sequences(inSequences) 
{
    std::cout << "Initilize multiple alignment" << std::endl;
    this->evolTree = NewickTreeFactory::deserialize(newickEvolTree);
    
	// Set distances parameters
	this->alignGapPenalty = 10;
	this->alignGapExtension = 2;
	
	// Create Align classes 
    std::ifstream matrixFile("data/blosum62.dat");
	if (!matrixFile) 
        throw "Error opening structure matrix file.";
        
    this->subMatrix = new SubMatrix(matrixFile);
    this->gapFunction = new AGPFunction(this->alignGapPenalty, this->alignGapExtension);    
}

ClustalWSample::ClustalWSample(const ClustalWSample& orig) 
{
    
}

ClustalWSample::~ClustalWSample() 
{
	delete(this->subMatrix);
	delete(this->gapFunction);
}

/// Execute the algorithm
std::vector<std::string> ClustalWSample::execute() 
{
    /// Launch recursive alignment.
    vector<string>* res = recursiveMultipleAlignment(this->evolTree->getRoot());
    vector<string>  valueRes = *res;
    delete res;
    return valueRes;
}

/// Recursive alignment method.
std::vector<std::string>*
ClustalWSample::recursiveMultipleAlignment(NewickTree::Node * evolNode) 
{
    /// If is a leaf return the sequence that references it
    if (evolNode->isLeaf()) 
    {
        intSet * seqIds = evolNode->getSequencesSet();
        /// Take the only element in the set.
        int seqId= *(seqIds->upper_bound(0));
        vector<string>* retVector = new vector<string>;
        retVector->push_back(this->sequences[seqId]);
        return retVector;
    }
    
   
    NewickTree::Node* leftChild = evolNode->getLeftChild();
    NewickTree::Node* rightChild = evolNode->getRightChild();
    
    vector<string>* leftChildAlign;
    vector<string>* rightChildAlign;
    /// Recursive call on the two children in order
    if (leftChild->getDistance() > rightChild->getDistance())
    {
        leftChildAlign  = recursiveMultipleAlignment(leftChild);
        rightChildAlign = recursiveMultipleAlignment(rightChild);
    }
    else
    {
        rightChildAlign = recursiveMultipleAlignment(rightChild);
        leftChildAlign  = recursiveMultipleAlignment(leftChild);
    }
    
    /// Create the multiple scoring scheme
    /// Non sense initialization. Will be assigned in the loop
    this->sequenceData = new SequenceData(2, "", "");
    this->scoringScheme = new ScoringM2M(this->subMatrix, leftChildAlign, rightChildAlign, sequenceData, 1.0);
    
    vector<string>* match = new vector<string>;
        
    /// Align all sequences from left node
    for(size_t i = 0; i < leftChildAlign->size(); i++)
    {   
        /// With all from the right node
        for (size_t j = 0; j < rightChildAlign->size(); j++)
        {
            sequenceData->setSequence((*leftChildAlign)[i],  1);
            sequenceData->setSequence((*rightChildAlign)[j], 2);
            align = new NWAlign(this->sequenceData, this->gapFunction, this->scoringScheme);
           
            align->doMatchScore(cout);
           
            /// If we score the alignment this way every alignment should give
            /// the same result. So we can assign just one time.
            vector<string> res = align->getMatch();
           
            if (i == 0) match->push_back(res[1]);
            if (j == 0) match->push_back(res[0]);
           
            delete align;
           
        }
    }
	
    /// Free memory
    delete this->scoringScheme;
    delete this->sequenceData;
    delete leftChildAlign;
    delete rightChildAlign;

    return match;
 }

} /* namespace */
